<script setup>
import {ref} from 'vue';
import CommonButton from "@/Music/components/commonButton.vue";
import {message} from "@/utils/message";
import {Delete, Minus, Plus} from "@element-plus/icons-vue";
import {useI18n} from "vue-i18n";


const numberToAudio = {
  "-6": "C3",
  "-5": "D3",
  "-4": "E3",
  "-3": "F3",
  "-2": "G3",
  "-1": "A3",
  "0": "B3",
  "1": "C4",
  "2": "D4",
  "3": "E4",
  "4": "F4",
  "5": "G4",
  "6": "A4",
  "7": "B4",
  "8": "C5",
  "9": "D5",
  "10": "E5",
  "11": "F5",
  "12": "G5",
  "13": "A5",
  "14": "B5",

};
const i18n = useI18n();
let song = ref('');
let duration = ref('');
let analysisSong = ref([]);
let analysisSpeed = ref([]);
let ifAnalyzed = ref(false);
let ifLoading = ref(true);
let ifSaving = ref(false);
let ifInjectShow = ref(false);
let ifPreview = ref(false);

let audioContext = new (window.AudioContext || window.webkitAudioContext)();
// const audioContext = new AudioContext();
ifLoading.value = false; // 确保在用户交互后设置为false



//region new test record to mp3
let mediaRecorder;
let audioChunks = ref([]);
let isRecording = ref(false);
let streamDestination = ref(undefined);
function startRecording() {
  audioContext = new (window.AudioContext || window.webkitAudioContext)();
  streamDestination.value = audioContext.createMediaStreamDestination();
  const stream = streamDestination.value.stream;
  mediaRecorder = new MediaRecorder(stream);

  mediaRecorder.ondataavailable = (event) => {
    audioChunks.value.push(event.data);
  };

  mediaRecorder.onstop = () => {
    const audioBlob = new Blob(audioChunks.value, { type: 'audio/wav' });
    const url = URL.createObjectURL(audioBlob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'recorded_audio.wav';
    a.click();
    audioChunks.value = [];
  };

  mediaRecorder.start();
  isRecording.value = true;
}

function stopRecording() {
  if (mediaRecorder) {
    mediaRecorder.stop();
    isRecording.value = false;
  }
}

function playNoteWithDelayInRecord(frequency, duration, startTime, callback) {
  const oscillator = audioContext.createOscillator();
  const gainNode = audioContext.createGain();
  const filter = audioContext.createBiquadFilter();

  oscillator.type = 'triangle';
  oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime + startTime);
  gainNode.gain.setValueAtTime(0, audioContext.currentTime + startTime);

  oscillator.connect(filter);
  filter.connect(gainNode);
  gainNode.connect(streamDestination.value);// 连接到 MediaStreamDestination

  filter.type = 'lowpass';
  filter.frequency.setValueAtTime(2000, audioContext.currentTime + startTime);

  const actualStartTime = audioContext.currentTime + startTime;
  oscillator.start(actualStartTime);

  gainNode.gain.linearRampToValueAtTime(0.5, actualStartTime);
  gainNode.gain.linearRampToValueAtTime(0.7, actualStartTime + 0.05);
  gainNode.gain.linearRampToValueAtTime(0.7, actualStartTime + duration - 0.05);
  gainNode.gain.linearRampToValueAtTime(0, actualStartTime + duration);

  oscillator.stop(actualStartTime + duration);

  if (callback) {
    const endTime = actualStartTime + duration;
    setTimeout(() => {
      callback(endTime);
    }, (endTime - audioContext.currentTime) * 1000);
  }
}


function playSongInRecord() {
  const melody = analysisSong.value;
  let speed = analysisSpeed.value;

  let time = 0;
  for (let i = 0; i < melody.length; i++) {
    for (let j = 0; j < melody[i].length; j++) {
      let note = melody[i][j];
      let speed_time = Number(speed[i][j]);
      if (note !== '' && note !== 'null' && note !== null && note !== undefined) {
        const frequency = noteToFrequency(note);

        if (frequency) {
          playNoteWithDelayInRecord(frequency, speed_time, time);
        }
      }

      time += speed_time;
    }
  }
  return time
}

function saveRecordToFile(){
  // 开始录音
  startRecording();
  ifSaving.value=true;
  // 播放音乐
  let recordedTime = playSongInRecord();

  if(recordedTime!==0){
    // 获取音乐的时长 并将时长替换下述的10000
    setTimeout(()=>{
      ifSaving.value=false
      stopRecording();
    },1000*recordedTime)
  }else{
    ifSaving.value=false;
    message.warning(i18n.t('message.audioNull'))
  }

}



//endregion

//录制音频的函数 方便后续把操作的内容保存为音频 废弃
function playAndRecordSong() {
  const melody = analysisSong.value;
  const speed = analysisSpeed.value;

  let time = 0;
  const playNextNote = (i, j) => {
    if (i >= melody.length) return; // 如果已经播放完所有行，停止播放

    if (j >= melody[i].length) {
      // 如果已经播放完当前行的所有音符，移动到下一行
      playNextNote(i + 1, 0);
      return;
    }

    const note = melody[i][j];
    const speed_time = Number(speed[i][j]);

    if (note !== '' && note !== 'null' && note !== null && note !== undefined) {
      const frequency = noteToFrequency(note);
      if (frequency) {
        playNoteWithDelay(frequency, speed_time, time, () => {
          time += speed_time; // 增加时间间隔，等待下一个音符
          playNextNote(i, j + 1); // 播放同一行的下一个音符
        });
      }
    }
  };

  playNextNote(0, 0); // 从第一行第一个音符开始播放
}

//region 可用函数
//获取音符频率
function noteToFrequency(note) {
  const A4 = 440;
  const notes = {
    'C': 1, 'C#': 1.05946, 'Db': 1.05946, 'D': 1.12246, 'D#': 1.18921, 'Eb': 1.18921,
    'E': 1.25992, 'F': 1.33482, 'F#': 1.41254, 'Gb': 1.41254, 'G': 1.49346, 'G#': 1.57788,
    'Ab': 1.57788, 'A': 1.66529, 'A#': 1.75647, 'Bb': 1.75647, 'B': 1.84577
  };

  // 修正正则表达式以正确匹配音符名称和八度数
  let [, noteName, octave] = note.match(/([A-G](?:#|b|♯|♭)?)(\d+)/);
  octave = parseInt(octave, 10);
  let baseNote = notes[noteName];

  if (!baseNote) {
    return null;
  }

  let frequency = A4 * Math.pow(2, (octave - 4)) * baseNote;
  return frequency;
}


let oscillator
let gainNode
let filter
function playNoteWithDelay(frequency, duration, startTime, callback) {
  oscillator = audioContext.createOscillator();
  gainNode = audioContext.createGain();
  filter = audioContext.createBiquadFilter();

  oscillator.type = 'triangle'; // moni
  // oscillator.type = 'sine'; // moni
  oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
  gainNode.gain.setValueAtTime(0, audioContext.currentTime); // 从静音开始

  oscillator.connect(filter);
  filter.connect(gainNode);
  gainNode.connect(audioContext.destination);

  filter.type = 'lowpass';
  filter.frequency.setValueAtTime(2000, audioContext.currentTime); // 设置低通滤波器的截止频率

  // 设置开始时间
  const actualStartTime = audioContext.currentTime + startTime;
  oscillator.start(actualStartTime);

  // 模拟ADSR包络
  gainNode.gain.linearRampToValueAtTime(0.5, actualStartTime); // Attack
  gainNode.gain.linearRampToValueAtTime(0.7, actualStartTime + 0.05); // Decay to Sustain
  gainNode.gain.linearRampToValueAtTime(0.7, actualStartTime + duration - 0.05); // Sustain
  gainNode.gain.linearRampToValueAtTime(0, actualStartTime + duration); // Release

  oscillator.stop(actualStartTime + duration);

  // Callback when the note is done playing
  // 在音符结束后调用回调函数
  if (callback) {
    const endTime = actualStartTime + duration;
    setTimeout(() => {
      callback(endTime);
    }, (endTime - audioContext.currentTime) * 1000);
  }

}

function stopSong() {
  window.location.reload()
}

function analyzeSong() {
  ifAnalyzed.value = false
  let parts = song.value.split(',')
  let durationParts = duration.value.split(',')
  if (parts.length === 1) {
    let newParts = song.value.split('，')
    parts = newParts
  }
  if (durationParts.length === 1) {
    let newParts = duration.value.split('，')
    durationParts = newParts
  }
  if (parts.length > 12) {
    message.warning(i18n.t('message.characterWarning'))
  } else if (parts.length !== 1 && durationParts.length !== 1 && parts.length === durationParts.length) {
    let audioList = []
    let speedList = []
    for (let i = 0; i < parts.length; i++) {
      audioList.push(numberToAudio[parts[i]])
      speedList.push(Number(durationParts[i]))
    }
    analysisSong.value.push(audioList)
    analysisSpeed.value.push(speedList)
    ifAnalyzed.value = true
  } else {
    message.error(i18n.t('message.numberWarning'))
    message.error(i18n.t('message.commaWarning'))
  }


}

//解析文件
function playMineMine() {
  analysisSong.value = [
    [null, "G4", "G4", "G4", "C5", "G4", "G4"],
    [null, "G4", "G4", "G4", "C5", "G4", "G4"],
    [null, "G4", "G4", "E4", "G4"],
    [null, "G4", "G4", "E4", "G4"],
    ["G4", "E4", "G4", "E4", "A4", "G4", "E4", "G4"],
    [null, "G4", "G4", "G4", "G4", "G4", "C5", "G4"],
    ["G4", "G4", "G4", "G4", "G4", "G4", "G4", "G4", "C5", "G4"],
    ["G4", "G4", "G4", "E4", "G4"],
    ["G4", "G4", "G4", "E4", "G4"],
    ["A4", "A4", "A4", "A4", "A4", "A4", "A4", "G4", "G4", "G4"],
    [null, "C5", "C5", null, "G4", "G4", "G4", "D5", "C5"],
    [null, "G4", "G4", "G4", "G4", "G4", "G4", "G4", "G4", "D5", "C5"],
    [null, "G4", "G4", null, "G4", "G4", "G4", "D5", "C5"],
    ["C5", "D5", "E5", "F5", "E5", "C5"],
    ["C5", "B4", "A4", "C5", "B4", "A4", "C5", "B4", "A4"],
    ["C5", "D5", "E5", "F5", "E5", "D5", null, "E5", "D5", null, "E5", "D5"],

  ]
  analysisSpeed.value = [
    [0.25, 0.125, 0.125, 0.25, 0.25, 0.5, 0.5],
    [0.25, 0.125, 0.125, 0.25, 0.25, 0.5, 0.5],
    [0.25, 0.125, 0.125, 0.25, 0.25],
    [0.25, 0.125, 0.125, 0.25, 0.25],
    [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25],
    [0.25, 0.25, 0.25, 0.25, 0.125, 0.125, 0.25, 0.5],
    [0.125, 0.125, 0.125, 0.125, 0.25, 0.125, 0.125, 0.25, 0.25, 0.5],
    [0.25, 0.125, 0.125, 0.25, 0.25],
    [0.25, 0.125, 0.125, 0.25, 0.25],
    [0.25, 0.125, 0.125, 0.25, 0.125, 0.125, 0.25, 0.25, 0.25, 0.25],
    [0.25, 0.25, 0.25, 0.25, 0.25, 0.125, 0.125, 0.25, 0.25],
    [0.25, 0.125, 0.125, 0.25, 0.125, 0.125, 0.25, 0.125, 0.125, 0.25, 0.25],
    [0.25, 0.25, 0.25, 0.25, 0.25, 0.125, 0.125, 0.25, 0.25],
    [0.25, 0.25, 0.25, 0.25, 0.5, 0.5],
    [0.25, 0.25, 0.5, 0.25, 0.25, 0.5, 0.25, 0.25, 0.5],
    [0.25, 0.25, 0.25, 0.25, 0.5, 0.25, 0.25, 0.5, 0.25, 0.25, 0.5, 0.5],
  ]
  // playSong()
}

function playSong() {

  const melody = analysisSong.value;
  let speed = analysisSpeed.value;

  let time = 0;
  for (let i = 0; i < melody.length; i++) {
    for (let j = 0; j < melody[i].length; j++) {
      let note = melody[i][j]
      let speed_time = Number(speed[i][j])
      if (note !== '' && note !== 'null' && note !== null && note !== undefined) {
        const frequency = noteToFrequency(note);

        if (frequency) {
          // 递归地播放每个音符，并在每个音符之间添加延迟 每次只播放一个音 要怎么把这些音组成的曲子记录下来
          playNoteWithDelay(frequency, speed_time, time);
        }
      }

      time += speed_time; // 增加时间间隔，等待下一个音符
    }
  }

}

function deleteSong(index) {
  analysisSong.value = analysisSong.value.filter((item, idx) => idx !== index)
  analysisSpeed.value = analysisSpeed.value.filter((item, idx) => idx !== index)
}

function addColumn(index) {
  analysisSong.value[index].push('')
  analysisSpeed.value[index].push(0)
}

function removeColumn(index) {
  analysisSong.value[index].pop()
  analysisSpeed.value[index].pop()
}

function saveSong() {
  // 将二维数组转换为JSON字符串
  var SongjsonString = JSON.stringify(analysisSong.value, null, 2); // 使用了美化输出，缩进为2
  // 创建一个Blob对象，类型为application/txt
  var Songblob = new Blob([SongjsonString], {type: 'application/txt'});
  // 创建一个链接元素
  var Songlink = document.createElement('a');
  Songlink.href = URL.createObjectURL(Songblob); // 创建一个URL对象
  Songlink.download = "analysisSong.txt"; // 设置下载文件名
  // 将链接添加到DOM中（不显示），并模拟点击以触发下载
  document.body.appendChild(Songlink);
  Songlink.click();
  // 清理DOM和URL对象
  document.body.removeChild(Songlink);
  URL.revokeObjectURL(Songlink.href);

  // 将二维数组转换为JSON字符串
  var SpeedjsonString = JSON.stringify(analysisSpeed.value, null, 2); // 使用了美化输出，缩进为2
  // 创建一个Blob对象，类型为application/txt
  var Speedblob = new Blob([SpeedjsonString], {type: 'application/txt'});
  // 创建一个链接元素
  var Speedlink = document.createElement('a');
  Speedlink.href = URL.createObjectURL(Speedblob); // 创建一个URL对象
  Speedlink.download = "analysisSpeed.txt"; // 设置下载文件名
  // 将链接添加到DOM中（不显示），并模拟点击以触发下载
  document.body.appendChild(Speedlink);
  Speedlink.click();
  // 清理DOM和URL对象
  document.body.removeChild(Speedlink);
  URL.revokeObjectURL(Speedlink.href);
}

//endregion

//限制每行的输入 限制输入的数量 或者可以自定义列的增加或减少
// 0 5551 55 0 5551 55 05535 05535 5353 6535
//-7,5,5,5,8,5,5
//0.25,0.125,0.125,0.25,0.25,0.5,0.5
//-7,5,5,5,8,5,5
//0.25,0.125,0.125,0.25,0.25,0.5,0.5
//-7,5,5,3,5
//0.25,0.125,0.125,0.25,0.25
//-7,5,5,3,5
//0.25,0.125,0.125,0.25,0.25
//5,3,5,3,6,5,3,5
//0.25,0.25,0.25,0.25,0.25,0.25,0.25,0.25


</script>

<template>
  <div id="musicalMake" v-loading="ifLoading">
    <div class="musical-header">
      <common-button :text="'MineMine'" @click="playMineMine" style="width: auto"></common-button>
      <common-button class="upload-musical" :text="$t('musicalMake.muInject')" @click="ifInjectShow=true"
                     style="width: auto"></common-button>
      <span>{{ $t('musicalMake.muInfo') }}</span>
      <common-button class="upload-musical" :text="$t('musicalMake.preview')" @click="ifPreview=true"
                     style="width: auto"></common-button>
    </div>

    <div class="musical-container" :element-loading-text="$t('load.saving')" v-loading.fullscreen="ifSaving">
      <div id="musical-editor">
        <div class="editor-input">
          <el-input v-model="song" :placeholder="$t('musicalMake.audioInputInfo')"></el-input>
          <el-input v-model="duration" :placeholder="$t('musicalMake.durationInputInfo')"></el-input>
          <common-button :text="$t('musicalMake.analyze')" @click="analyzeSong"></common-button>
          <!--          <common-button :text="'解析文件'" @click="analyzeSongFile"></common-button>-->
          <common-button :text="$t('musicalMake.play')" @click="playSong"></common-button>
          <common-button :text="$t('musicalMake.refresh')" @click="stopSong"></common-button>
          <common-button :text="$t('musicalMake.save')" @click="saveSong" style="width: auto"></common-button>
          <common-button :text="$t('musicalMake.saveRecordToFile')" @click="saveRecordToFile" style="width: auto"></common-button>
        </div>
        <div class="editor-container">
          <div v-for="(item,index) in analysisSong" :key="index"
               style="display: flex;gap:10px;align-items: center;margin-bottom: 10px">
            <div style="display: grid;gap:10px;flex:1" :style="'grid-template-columns: repeat('+item.length+',1fr)'">
              <el-input v-for="(inner,inner_index) in item" :key="inner_index"
                        v-model="analysisSong[index][inner_index]"></el-input>
              <el-input v-for="(speed_inner,speed_inner_index) in analysisSpeed[index]" :key="speed_inner_index"
                        v-model="analysisSpeed[index][speed_inner_index]"></el-input>
            </div>
            <!--            <div style="display: grid;gap:10px;flex:1" :style="'grid-template-columns: repeat(4,1fr)'">-->
            <!--              <div v-for="(inner,inner_index) in item" :key="inner_index" style="display: flex;gap: 10px">-->
            <!--                <el-input v-model="analysisSong[index][inner_index]"></el-input>-->
            <!--                <el-input v-model="analysisSpeed[index][inner_index]"></el-input>-->
            <!--              </div>-->
            <!--            </div>-->
            <el-icon class="icon" @click="addColumn(index)">
              <Plus/>
            </el-icon>
            <el-icon class="icon" @click="removeColumn(index)">
              <Minus/>
            </el-icon>
            <el-icon class="icon" @click="deleteSong(index)">
              <Delete/>
            </el-icon>
          </div>
        </div>
      </div>
      <div id="musical-preview" v-if="ifPreview">
        <div class="header">
          <span class="close-preview-span" @click="ifPreview = false">{{ $t('modal.close') }}</span>
          {{ $t('musicalMake.preview') }}
        </div>
        <div class="preview-info">{{$t('musicalMake.previewInfo')}}</div>
        <div class="preview-content">

          <div v-for="(item,index) in analysisSpeed" :key="index"
               style="display: flex;gap:10px;align-items: center;justify-content:center;margin-bottom: 10px">
              <div v-for="(speed_inner,speed_inner_index) in analysisSpeed[index]" :key="speed_inner_index">
                <span :style="{
                  borderBottom:analysisSpeed[index][speed_inner_index]===0.5?'1px solid #000':
                  analysisSpeed[index][speed_inner_index]===0.25?'1px dashed #000':
                  analysisSpeed[index][speed_inner_index]===0.125?'1px dotted #000':'0px'
                }">{{analysisSong[index][speed_inner_index]}}</span>
              </div>
          </div>

        </div>
      </div>
    </div>

    <div class="musical-footer">

    </div>

    <el-dialog
        v-model="ifInjectShow"
        :title="$t('musicalMake.muInject')"
        :width="400"
        draggable
    >
      <div style="display: grid;grid-template-columns: 1fr 1fr 1fr;gap:10px">
        <span>"-6":"C3"</span>
        <span>"-5":"D3"</span>
        <span>"-4":"E3"</span>
        <span>"-3":"F3"</span>
        <span>"-2":"G3"</span>
        <span>"-1":"A3"</span>
        <span>"0":"B3"</span>
        <span>"1":"C4"</span>
        <span>"2":"D4"</span>
        <span>"3":"E4"</span>
        <span>"4":"F4"</span>
        <span>"5":"G4"</span>
        <span>"6":"A4"</span>
        <span>"7":"B4"</span>
        <span>"8":"C5"</span>
        <span>"9":"D5"</span>
        <span>"10":"E5"</span>
        <span>"11":"F5"</span>
        <span>"12":"G5"</span>
        <span>"13":"A5"</span>
        <span>"14":"B5"</span>
      </div>
      <template #footer>
        <div class="dialog-footer" style="display: flex;justify-content: flex-end;">
          <common-button @click="ifInjectShow = false" :text="$t('modal.close')"></common-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped>
::v-deep .el-button + .el-button {
  margin: 0;
}

#musicalMake {
  background-color: #fff;
  height: 100%;
  border-radius: 10px;
  box-shadow: 0 0 5px 1px #ffffff;


  .musical-header {
    display: flex;
    gap: 10px;
    align-items: center;
    padding: 15px;
    height: 8%;

    .upload-musical {
      display: flex;
      align-items: center;
    }

    span {
      color: #8f8f8f;
    }
  }

  .musical-container {
    display: grid;
    grid-template-columns: 1fr;
    padding: 10px;
    height: 84%;
    overflow: auto;

    .musical-item {
      box-shadow: -3px 0px 5px 0px var(--main-color);
      border-radius: 4px;
      margin: 5px;
      padding: 15px;

      .musical-img {
        max-width: 50%;
      //height: 70%; //width: fit-content; //margin: auto;

        .inner-img {
          height: fit-content;
          width: 70%;
          margin-bottom: 10px;
          box-shadow: -3px 0px 5px 0px var(--main-color);
        }

        .musical-name {
          display: block;
          width: 180px;
          overflow-x: hidden;
          text-align: center;
          white-space: nowrap;
          text-overflow: ellipsis;
          transition: all 100ms ease-in-out;

          &:hover {
            cursor: pointer;
            color: var(--main-color);
            scale: 1.05;
          }
        }

      }

      .musical-basicInfo {
        width: 50%;
        height: fit-content;
        margin: auto;
        display: grid;
        gap: 5px;

        span {
          padding: 2px;
          font-weight: bold;
          color: #8f8f8f;
        }
      }

      .musical-operation {
        height: 20%;
        display: flex;
        justify-content: center;
        gap: 15px;
        border: 1px solid #c6c6c611;
        box-shadow: 0 0 1px 0px #fff;
        border-radius: 6px;
        color: var(--main-color);
        font-weight: bold;
        align-items: center;

        .musical-op-item {
          display: flex;
          align-items: center;
          gap: 10px;
          transition: all 0.2s ease-in-out;

          .musical-icon {
            width: 15px;
            height: fit-content;
          }
        }

        .musical-op-item:hover {
          color: var(--main-color);
          cursor: pointer;
          scale: 1.1;
        }


      }
    }

  }

  .musical-footer {
    border-top: 1px dashed rgba(38, 96, 222, 0.64);
    width: 98%;
    margin: auto;
    height: 8%;
    padding: 10px;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
  }


}

#musical-editor {
  .editor-input {
    display: flex;
    gap: 10px;
  }

  .editor-container {
    margin-top: 10px;

    .icon {

      &:hover {
        cursor: pointer;
        scale: 1.2;
        color: var(--main-color);
      }
    }
  }
}

#musical-preview {
  position: absolute;
  width: 100vw;
  height: 100vh;
  min-width: 1080px;
  top: 0;
  left: 0;
  background-color: #fff;

  .header {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 6vh;
    font-weight: bold;
    letter-spacing: 1px;

    .close-preview-span {
      position: absolute;
      left: 10px;
      font-size: 13px;
      opacity: .9;
      transition: all 100ms ease-in-out;

      &:hover {
        cursor: pointer;
        font-size: 14px;
        opacity: 1;
      }
    }
  }

  .preview-info{
    padding: 10px;
  }

  .preview-content {
    padding: 10px;
    display: grid;
    grid-template-columns: repeat(4,1fr);
  }


}
</style>
